package com.sotnyk.sspeed.visual;

import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.sotnyk.sspeed.R;
import com.sotnyk.sspeed.utils.ExceedingState;
import com.sotnyk.sspeed.utils.FixState;
import com.sotnyk.sspeed.utils.ITtsManager;
import com.sotnyk.sspeed.utils.SpeedUnitsConverter;
import com.sotnyk.sspeed.utils.SsAppState;
import com.sotnyk.sspeed.views.SpeedTextView;

import java.io.IOException;

public class MainSSpeedFragment extends Fragment implements LocationListener,
        GpsStatus.Listener, SpeedLimitDialogFragment.SpeedLimitDialogListener {

    private final static String TAG = "MainSSpeedFragment";

    private Context mContext;
    private static SsAppState sState;
    private static SpeedUnitsConverter sSpeedUnitsConverter;

    private SpeedTextView mSpeedometerTextView;
    private RadioGroup mLimitsButtonsRadioGroup;
    private RadioButton mLimit0RadioButton;
    private RadioButton mLimit1RadioButton;
    private int[] mLimitButtonsIds;

    private String[] mSpeedUnitsSuffixes;

    private LocationManager mLocationManager;
    private final String mProvider = LocationManager.GPS_PROVIDER;

    private long mLastLocationMillis;
    private final static int MAX_SPEED_ACTUAL_MSEC = 3000;
    private final static int MAX_SPEED_AVAILABLE_MSEC = 6000;
    private final static int UPDATE_INTERVAL_MSEC = 1000;
    private FixState mGpsFixState = FixState.NOT_AVAILABLE;
    private FixState mPrevGpsFixState = FixState.NOT_AVAILABLE;
    private Location mLastLocation;
    private ExceedingState mExceedingState = ExceedingState.NOT_AVAILABLE;

    private final Handler mUpdateHandler = new Handler();

    private MediaPlayer mMediaPlayer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        mContext = getActivity();
        sState = SsAppState.get(mContext);
        sSpeedUnitsConverter = new SpeedUnitsConverter(sState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_main, parent, false);

        // http://stackoverflow.com/questions/10165108/android-prevent-display-from-turning-off
        getActivity().getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        initControls(v);

        initLocationServices();
        mLimitsButtonsRadioGroup.check(mLimitButtonsIds[sState
                .getSelectedLimit() + 1]);

        mSpeedUnitsSuffixes = getResources().getStringArray(
                R.array.speed_units_list);

        return v;
    }

    private void initLocationServices() {
        // http://www.vogella.com/tutorials/AndroidLocationAPI/article.html
        // http://velmuruganandroidcoding.blogspot.com/2014/01/get-current-user-speed-using-gps-android.html
        // http://stackoverflow.com/questions/6720974/how-to-get-information-about-the-satellites-from-android/6721071#6721071
        // http://stackoverflow.com/questions/2021176/how-can-i-check-the-current-status-of-the-gps-receiver
        mLocationManager = (LocationManager) mContext.getSystemService(
                Context.LOCATION_SERVICE);
        showSettingsAlert();
    }

    @Override
    public void onResume() {
        super.onResume();
        sState.refreshState();
        mLocationManager.requestLocationUpdates(mProvider, 400, 1, this);
        mLocationManager.addGpsStatusListener(this);
        mUpdateHandler.postDelayed(UpdateControlsTask, UPDATE_INTERVAL_MSEC);
    }

    @Override
    public void onPause() {
        stopSound();
        mUpdateHandler.removeCallbacks(UpdateControlsTask);
        mLocationManager.removeGpsStatusListener(this);
        mLocationManager.removeUpdates(this);
        super.onPause();
    }

    private void initControls(View v) {
        mSpeedometerTextView = (SpeedTextView) v
                .findViewById(R.id.speedometer_textview);

        mLimitsButtonsRadioGroup = (RadioGroup) v
                .findViewById(R.id.limits_buttons_radiogroup);
        mLimitsButtonsRadioGroup
                .setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

                    @Override
                    public void onCheckedChanged(RadioGroup group, int checkedId) {
                        for (int i = 0; i < mLimitButtonsIds.length; ++i) {
                            if (mLimitButtonsIds[i] == checkedId) {
                                sState.setSelectedLimit(i - 1);
                                break;
                            }
                        }
                    }
                });

        mLimit0RadioButton = (RadioButton) v.findViewById(R.id.limit0_button);
        mLimit0RadioButton
                .setOnLongClickListener(new View.OnLongClickListener() {

                    @Override
                    public boolean onLongClick(View v) {
                        processLongLimitClick(0);
                        return true;
                    }
                });
        mLimit1RadioButton = (RadioButton) v.findViewById(R.id.limit1_button);
        mLimit1RadioButton
                .setOnLongClickListener(new View.OnLongClickListener() {

                    @Override
                    public boolean onLongClick(View v) {
                        processLongLimitClick(1);
                        return true;
                    }
                });
        mLimitButtonsIds = new int[]{R.id.infinite_button,
                R.id.limit0_button, R.id.limit1_button};
        updateText();
    }

    private void processLongLimitClick(int limitNumber) {
        SpeedLimitDialogFragment speedLimitDialog = new SpeedLimitDialogFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(SpeedLimitDialogFragment.LIMIT_NUMBER_PARAM, limitNumber);
        bundle.putInt(SpeedLimitDialogFragment.SPEED_LIMIT_PARAM,
                sState.getSpeedLimits(limitNumber));
        speedLimitDialog.setArguments(bundle);
        speedLimitDialog.setListener(this);
        speedLimitDialog.show(getFragmentManager(), "Limits");
    }

    private void updateText() {
        mLimit0RadioButton.setText("" + sState.getSpeedLimits(0));
        mLimit1RadioButton.setText("" + sState.getSpeedLimits(1));
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        sState.saveState();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location == null)
            return;

        mLastLocation = location;
        mLastLocationMillis = SystemClock.elapsedRealtime();

        updateSpeed();
    }

    private void updateSpeed() {
        checkGpsActuality();
        mSpeedometerTextView.setExceedingState(mExceedingState);
        if (mLastLocation == null || mGpsFixState == FixState.NOT_AVAILABLE) {
            mSpeedometerTextView.setText(R.string.not_available);
        } else if (mGpsFixState == FixState.NOT_ACTUAL) {
            mSpeedometerTextView.setText(lastSpeedToText());
        } else {
            mSpeedometerTextView.setText(lastSpeedToText());
        }
    }

    private String lastSpeedToText() {
        return sSpeedUnitsConverter.speedToText(mLastLocation.getSpeed(), mSpeedUnitsSuffixes);
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {
        Toast.makeText(mContext, "Enabled provider " + provider,
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onProviderDisabled(String provider) {
        Toast.makeText(mContext, "Disabled provider " + provider,
                Toast.LENGTH_SHORT).show();

    }

    @Override
    public void onGpsStatusChanged(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                checkGpsActuality();
                break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
                checkGpsActuality();
                break;
        }
        updateSpeed();
    }

    private void checkGpsActuality() {
        if (mLastLocation == null) {
            mGpsFixState = FixState.NOT_AVAILABLE;
        } else {
            long lastFixationDuration = SystemClock.elapsedRealtime()
                    - mLastLocationMillis;
            if (lastFixationDuration < MAX_SPEED_ACTUAL_MSEC) {
                mGpsFixState = FixState.FIXED;
            } else if (lastFixationDuration < MAX_SPEED_AVAILABLE_MSEC) {
                mGpsFixState = FixState.NOT_ACTUAL;
            } else {
                mGpsFixState = FixState.NOT_AVAILABLE;
            }
        }

        if (sState.isSaySatellitesState()) {
            if (mPrevGpsFixState == FixState.NOT_AVAILABLE
                    && mGpsFixState == FixState.FIXED) {
                speakIfSilence(getString(R.string.speech_satellites_fixed));
            } else if (mPrevGpsFixState != FixState.NOT_AVAILABLE
                    && mGpsFixState == FixState.NOT_AVAILABLE) {
                speakIfSilence(getString(R.string.speech_satellites_lost));
            }
        }
        mPrevGpsFixState = mGpsFixState;
    }

    private final Runnable UpdateControlsTask = new Runnable() {
        @Override
        public void run() {
            updateSpeed();
            checkSpeedExceed();
            mUpdateHandler.postDelayed(this, UPDATE_INTERVAL_MSEC);
        }
    };

    private void checkSpeedExceed() {
        if (mGpsFixState == FixState.NOT_AVAILABLE) {
            mExceedingState = ExceedingState.NOT_AVAILABLE;
        } else if (mGpsFixState == FixState.NOT_ACTUAL) {
            mExceedingState = ExceedingState.NOT_ACTUAL;
        } else if (mGpsFixState == FixState.FIXED) {
            mExceedingState = ExceedingState.IN_LIMITS;
            if (mLastLocation == null)
                return;
            if (sState.getSelectedLimit() < 0)
                return;
            float speedInCurrentUnits = sSpeedUnitsConverter.speedInCurrentUnits(mLastLocation
                    .getSpeed());
            float currentSpeedLimit = sState.getSpeedLimits(sState.getSelectedLimit());
            if (speedInCurrentUnits > currentSpeedLimit * sState.getWarningLimit() &&
                    speedInCurrentUnits < currentSpeedLimit) {
                mExceedingState = ExceedingState.NEAR_LIMIT;
            }
            if (speedInCurrentUnits > currentSpeedLimit) {
                mExceedingState = ExceedingState.LIMIT_EXCEEDED;
                if (sState.isUseSpeech()) {
                    speakIfSilence("" + (int) speedInCurrentUnits);
                } else {
                    // http://stackoverflow.com/questions/10460499/how-to-play-ringtone-selected-from-ringtonepreference
                    Uri uri = Uri.parse(sState.getAlarmSound());
                    playSound(uri);
                }
                //mSpeedometerTextView.startAlarmAnimation();
            }
        }
    }

    private void showSettingsAlert() {
        // http://blog.vogella.com/2012/04/30/android-how-to-check-if-a-locationprovider-is-available/
        if (!mLocationManager.isProviderEnabled(mProvider)) {
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            startActivity(intent);
        }
    }

    @Override
    public void onDialogPositiveClick(SpeedLimitDialogFragment dialog) {
        sState.setSpeedLimits(dialog.getLimitNumber(), dialog.getSpeedLimit());
        sState.saveState();
        updateText();
    }

    private void stopSound() {
        if (mMediaPlayer != null) {
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.stop();
                mMediaPlayer = null;
            }
        }
    }

    private void playSound(Uri alert) {
        if (mMediaPlayer != null) {
            if (mMediaPlayer.isPlaying()) {
                return;
            }
        }
        mMediaPlayer = new MediaPlayer();
        try {
            mMediaPlayer.setDataSource(mContext, alert);
            final AudioManager audioManager = (AudioManager) mContext
                    .getSystemService(Context.AUDIO_SERVICE);
            if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
            }
        } catch (IOException e) {
            Log.e(TAG, "" + e);
        }
    }

    private void speakIfSilence(String text) {
        if (!sState.isUseSpeech())
            return;
        Activity a = getActivity();
        if (a instanceof ITtsManager) {
            ITtsManager ttsManager = (ITtsManager) getActivity();
            ttsManager.speakIfSilence(text);
        }
    }
}
